package club.jdiy.dev.zlt.service.impl;

import club.jdiy.admin.util.IdUtil;
import club.jdiy.core.base.JDiyBaseService;
import club.jdiy.core.ex.JDiyException;
import club.jdiy.core.ex.JDiyFormException;
import club.jdiy.dev.dao.JDiyUiDao;
import club.jdiy.dev.entity.JDiyUi;
import club.jdiy.dev.meta.*;
import club.jdiy.dev.types.BtnActTpl;
import club.jdiy.dev.types.BtnTargetTpl;
import club.jdiy.dev.types.ColFormatTpl;
import club.jdiy.dev.types.UiTpl;
import club.jdiy.dev.view.FtlParser;
import club.jdiy.dev.zlt.service.JDevTrPageService;
import club.jdiy.utils.ArrayUtils;
import club.jdiy.utils.JsonUtils;
import club.jdiy.utils.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

@Service
public class JDevTrPageServiceImpl extends JDiyBaseService<JDiyUi, JDiyUiDao, String>
        implements JDevTrPageService {
    private static final Pattern update_code_p = Pattern.compile("^(\\s*`*[a-zA-Z0-9_]+?`*\\s*=\\s*'*[^'=]+?'*\\s*)(\n(\\s*`*[a-zA-Z0-9_]+?`*\\s*=\\s*'*[^'=]+?'*\\s*))*$");

    @Override
    @Transactional
    public JDiyUi logicSave(JDiyUi vo) {
        JDiyUi po = dao.findById(vo.getId()).orElse(null);
        if (po == null) {
            po = new JDiyUi();
            po.setType(vo.getType());
            po.setId(IdUtil.newId());
            po.setBindType(vo.getBindType());
            if ("table".equals(vo.getBindType())) {
                po.setMainTable(vo.getMainTable());
            } else {
                po.setMainEntity(vo.getMainEntity());
                po.setMainTable(context.getDao().getEntityMeta(vo.getMainEntity()).getTableName());
            }

            TreeUiMeta lpv = new TreeUiMeta();

            JDiyUi inPage = dao.findFirstByTypeAndMainTable(UiTpl.form, po.getMainTable());

            ButtonMeta b0 = new ButtonMeta();
            b0.setId(IdUtil.newId());
            b0.setText("添加");
            b0.setIcon("layui-icon-add-1");
            b0.setAct(BtnActTpl.add);
            b0.setWidth(640);
            b0.setHeight(480);
            b0.setTarget(BtnTargetTpl.dialog);
            if (inPage != null) {
                b0.setGoPageId(inPage.getId());
                b0.setGoPageType(inPage.getType());
            }
            lpv.setTopButtons(new ButtonMeta[]{b0});

            ButtonMeta b1 = new ButtonMeta();
            b1.setId(IdUtil.newId());
            b1.setText("修改");
            b1.setIcon("layui-icon-edit");
            b1.setDbl(true);
            b1.setWidth(640);
            b1.setHeight(480);
            b1.setAct(BtnActTpl.edit);
            b1.setTarget(BtnTargetTpl.dialog);
            if (inPage != null) {
                b1.setGoPageId(inPage.getId());
                b1.setGoPageType(inPage.getType());
            }

            ButtonMeta b2 = new ButtonMeta();
            b2.setId(IdUtil.newId());
            b2.setText("删除");
            b2.setIcon("layui-icon-delete");
            b2.setAct(BtnActTpl.del);
            b2.setTarget(BtnTargetTpl.ajaxTodo);
            b2.setBg("#ff5722");
            b2.setConfirm("您真的要删除此条记录吗？");


            ButtonMeta b3 = new ButtonMeta();
            b3.setId(IdUtil.newId());
            b3.setText("添加子项");
            b3.setIcon("layui-icon-add-1");
            b3.setDbl(false);
            b3.setAct(BtnActTpl.edit);
            b3.setWidth(640);
            b3.setHeight(480);
            b3.setTarget(BtnTargetTpl.dialog);
            if (inPage != null) {
                b3.setGoPageId(inPage.getId());
                b3.setGoPageType(inPage.getType());
            }

            lpv.setRowButtons(new ButtonMeta[]{b1, b2, b3});


            ColumnMeta c1 = new ColumnMeta();
            c1.setId(IdUtil.newId());
            c1.setField("name");
            c1.setLabel("名称");
            c1.setAlign("left");
            c1.setFormat(ColFormatTpl.text);

            ColumnMeta c2 = new ColumnMeta();
            c2.setId(IdUtil.newId());
            c2.setField("sortIndex");
            c2.setLabel("排序索引");
            c2.setWidth(100);
            c2.setFormat(ColFormatTpl.text);

            lpv.setColumns(new ColumnMeta[]{c1, c2});
            lpv.setMgeWidth(300);

            po.setMetaData(JsonUtils.stringify(lpv));
        }
        po.setName(vo.getName());
        po = dao.save(po);

        return po;
    }

    @Override
    @Transactional
    public void saveDesign(String uid, TreeUiMeta uiMeta) {
        JDiyUi ui = dao.findById(uid).orElseThrow(()
                -> new JDiyFormException("列表界面信息不存在或被删除(uiId=" + uid + ")", ""));
        if (StringUtils.isEmpty(uiMeta.getPageHandler())) uiMeta.setPageHandler(null);
        if (uiMeta.getColumns() == null || uiMeta.getColumns().length < 1)
            throw new JDiyFormException("列表中至少需要配置一个显示字段", "");
        if (StringUtils.isEmpty(uiMeta.getNameField())) throw new JDiyException("您还没有定义表格显示的[树形列]，请先在右侧边栏[全局设置]里配置。");
        if (StringUtils.isEmpty(uiMeta.getPidField()))
            throw new JDiyException("您还没有定义树的父级ID列(parentId)，请先在右侧边栏[全局设置]里配置。");
        if (StringUtils.isEmpty(uiMeta.getPathField()))
            throw new JDiyException("您还没有定义树的路径列，请先在右侧边栏[全局设置]里配置。");
        if (uiMeta.getPidField().equals(uiMeta.getPathField()))
            throw new JDiyException("父级ID列和路径列不能是同一个。");
        if (StringUtils.isEmpty(uiMeta.getSortField())) uiMeta.setSortField("id");
        __saveCols(uiMeta);
        __saveBtns(uiMeta.getTopButtons(), 0);
        if (uiMeta.isMge()) __saveBtns(uiMeta.getRowButtons(), 1);
        ui.setMetaData(JsonUtils.stringify(uiMeta));
        dao.save(ui);
        dao.updateMenuRealms(uid, ArrayUtils.join(dao.getRealms(uid), "\n"));
    }

    private void __saveCols(TreeUiMeta uiMeta) {
        Set<String> uniqueCheck = new HashSet<>();
        for (ColumnMeta vo : uiMeta.getColumns()) {
            if (!StringUtils.isEmpty(vo.getField())) {
                if (uniqueCheck.contains(vo.getField()))
                    throw new JDiyFormException("表格列显示字段(" + vo.getField() + ")重复绑定了。请修改或移除此列 。", "jdiylsoth_" + vo.getId());
                uniqueCheck.add(vo.getField());
            }
            switch (vo.getFormat()) {
                case kv:
                    if (StringUtils.isEmpty(vo.getKv()))
                        throw new JDiyFormException("选中的列为“键值转键名”的显示方式，但[键值:键名]列表没有填写。填写格式详见配置框右侧帮助。", "jdiylsoth_" + vo.getId());
                    for (String s : vo.getKv().trim().split("\n")) {
                        if (StringUtils.isEmpty(s)) continue;
                        int n = s.split(":").length;
                        if (n < 2 || n > 3)
                            throw new JDiyFormException("选中的列[键值:键名]列表输入格式不正确。详见配置框右侧帮助。", "jdiylsoth_" + vo.getId());
                    }
                    break;
                case dict:
                    if (StringUtils.isEmpty(vo.getDictId()))
                        throw new JDiyFormException("选中的列为“数据字典”的显示方式，但您还没有选择字典．", "jdiylsoth_" + vo.getId());
                    break;
                case oneToMany:
                    if (StringUtils.isEmpty(vo.getJoinTable()))
                        throw new JDiyFormException("选中的列为外联表显示，但您还没有选择外联表。", "jdiylsoth_" + vo.getId());
                    if (StringUtils.isEmpty(vo.getJoinRef()))
                        throw new JDiyFormException("选中的列为(一对多)外联表显示，但您还没有选择外键。", "jdiylsoth_" + vo.getId());
                    if (StringUtils.isEmpty(vo.getJoinField()))
                        throw new JDiyFormException("选中的列为外联表显示，但您还没有选择要显示的外联表字段。", "jdiylsoth_" + vo.getId());
                    break;
                case manyToOne:
                    if (StringUtils.isEmpty(vo.getJoinTable()))
                        throw new JDiyFormException("选中的列为(一对多)外联表显示，但您还没有选择外联表。", "jdiylsoth_" + vo.getId());
                    if (StringUtils.isEmpty(vo.getJoinField()))
                        throw new JDiyFormException("选中的列为(一对多)外联表显示，但您还没有选择显示字段。", "jdiylsoth_" + vo.getId());
                    break;
                case manyToMany:
                    if (StringUtils.isEmpty(vo.getJoinTable()))
                        throw new JDiyFormException("选中的列为(多对多)外联表显示，但您还没有选择外联表。", "jdiylsoth_" + vo.getId());
                    if (StringUtils.isEmpty(vo.getJoinField()))
                        throw new JDiyFormException("选中的列为(多对多)外联表显示，但您还没有选择显示字段。", "jdiylsoth_" + vo.getId());
                    if (StringUtils.isEmpty(vo.getManyTable()))
                        throw new JDiyFormException("选中的列为(多对多)外联表显示，但您还没有选择中间表。", "jdiylsoth_" + vo.getId());
                    if (StringUtils.isEmpty(vo.getManyField0()))
                        throw new JDiyFormException("选中的列为(多对多)外联表显示，但您还没有选择中间表的正向关联字段。", "jdiylsoth_" + vo.getId());
                    if (StringUtils.isEmpty(vo.getManyField1()))
                        throw new JDiyFormException("选中的列为(多对多)外联表显示，但您还没有选择中间表的反向关联字段。", "jdiylsoth_" + vo.getId());
                    break;
                case tree:
                    if (StringUtils.isEmpty(vo.getTreeId()))
                        throw new JDiyFormException("选中的列为“外联树路径层级”的显示方式，但您还没有选择树形。", "jdiylsoth_" + vo.getId());
                case img:
                case file:
                    vo.setAlign("center");
            }
            vo.setAlign(StringUtils.isEmpty(vo.getAlign()) ? "center" : vo.getAlign());
        }
        if (!uniqueCheck.contains(uiMeta.getNameField()))
            throw new JDiyException("右侧栏[全局设置]中[树形显示=" + uiMeta.getNameField() + "],但设计区表格里面还没有该字段，请修改。");

    }

    private void __saveBtns(ButtonMeta[] buttons, int pos) {
        for (ButtonMeta vo : buttons) {
            if (StringUtils.isEmpty(vo.getText()) && StringUtils.isEmpty(vo.getIcon())) {
                throw new JDiyFormException("选中的按钮“按钮文字”或者“显示图标”至少需要设置一个。", "jdiylsobj_" + vo.getId());
            }
            if (BtnActTpl.add == vo.getAct() || BtnActTpl.edit == vo.getAct() || BtnActTpl.view == vo.getAct() || BtnActTpl.page == vo.getAct() || BtnActTpl.link == vo.getAct()) {
                if (BtnActTpl.link != vo.getAct()) {
                    if (StringUtils.isEmpty(vo.getGoPageId()))
                        throw new JDiyFormException("选中的按钮未设置要打开的目标界面！", "jdiylsobj_" + vo.getId());
                    else {
                        try {
                            UiMeta uim = dao.getUiMeta(vo.getGoPageId());
                            vo.setGoPageType(uim.getType());
                        } catch (Exception ex) {
                            ex.printStackTrace();
                            throw new JDiyFormException("获取目标界面类型失败！", "jdiylsobj_" + vo.getId());
                        }
                    }
                    if (BtnTargetTpl._blank == vo.getTarget()) {
                        vo.setTarget(BtnTargetTpl.dialog);
                    }

                }
                if (BtnActTpl.link == vo.getAct() && StringUtils.isEmpty(vo.getOutLink()))
                    throw new JDiyFormException("目标按钮未设定外链地址。", "jdiylsobj_" + vo.getId());
                if (BtnTargetTpl.dialog == vo.getTarget()) {
                    if (vo.getWidth() == null || vo.getWidth() < 300)
                        throw new JDiyFormException("目标按钮弹框宽度设置不正确(建议设置为300~960之间)。", "jdiylsobj_" + vo.getId());
                    if (vo.getHeight() == null || vo.getHeight() < 150)
                        throw new JDiyFormException("目标按钮弹框高度设置不正确(建议设置为180~720之间)。", "jdiylsobj_" + vo.getId());
                }
            } else {
                vo.setTarget(BtnTargetTpl.ajaxTodo);
                if (BtnActTpl.update == vo.getAct()) {
                    if (StringUtils.isEmpty(vo.getUpdateCode()))
                        throw new JDiyFormException("目标按钮操作类型为“更新字段值”，但未填写要更新的字段内容。请在右侧属性栏中修改。", "jdiylsobj_" + vo.getId());
                    if (!update_code_p.matcher(vo.getUpdateCode()).matches())
                        throw new JDiyFormException("目标按钮更新的字段内容格式输入不正确，需要为：“字段名=值”的格式，一行填一个，中间不要出现多个空行或其它无关字符。", "jdiylsobj_" + vo.getId());
                    String ss = vo.getUpdateCode().trim().replaceAll("\\s*,*\\s*([\\n\\r])+\\s*,*\\s*", "\n");
                    vo.setUpdateCode(ss);
                } else if (BtnActTpl.ajax == vo.getAct()) {
                    if (StringUtils.isEmpty(vo.getAjaxUrl()))
                        throw new JDiyFormException("选中的按钮未配置要请求的自定义ajax页面地址。", "jdiylsobj_" + vo.getId());
                }
            }
            if (StringUtils.hasText(vo.getGrantCode()) && !JDevLsPageServiceImpl.codeP.matcher(vo.getGrantCode()).matches()) {
                throw new JDiyFormException("按钮权限代码只能为英文字母(数字或下划线组合），且不要以数字开头(符合变量命名规范)", "jdiylsobj_" + vo.getId());
            }
            if (StringUtils.hasText(vo.getConditionShow())) {
                if (vo.getConditionShow().contains("${") || vo.getConditionShow().contains("<>"))
                    throw new JDiyFormException("按钮显示条件配置可能有误，请检查。", "jdiylsobj_" + vo.getId());
            }
            if (pos == 1 && !StringUtils.hasText(vo.getDepthShow())) vo.setDepthShow("a");
        }
    }
}
